﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Text.RegularExpressions;

namespace TestGenerator
{
    class Test
    {
        public int answersNumber = 5;
        public int questionsNumber = 30;
        private int numberOfQuestions;
        private static Dictionary<int, List<char>> answers = new Dictionary<int, List<char>>();        
        private Dictionary<int, QuestionForTest> testQuestions;   

        private struct QuestionForTest
        {
            public string question;
            public string[] answers;
            public int correct;
            public int number;

            public QuestionForTest(int ansNumber)
            {
                this.question = String.Empty;
                this.answers = new string[ansNumber];
                this.correct = -1;
                this.number = 0;
            }

            public QuestionForTest(string question, string[] answers, int correct, int number)
            {
                this.question = question;
                this.answers = new string[answers.Length];
                for (int i = 0; i < answers.Length; i++)
                {
                    this.answers[i] = answers[i];
                }
                this.correct = correct;
                this.number = number;
            }
        }

        public Test(Dictionary<string, Question> allQuestions)
        {
            this.numberOfQuestions = 0;
            this.testQuestions = new Dictionary<int, QuestionForTest>();
            Random rand = new Random();
            if (allQuestions != null)
            {
                if (allQuestions.Count > questionsNumber)
                {
                    HashSet<int> addedQuestions = new HashSet<int>();
                    Random randQuestion = new Random();
                    while (addedQuestions.Count < questionsNumber)
                    {
                        int questionIndex = randQuestion.Next(0, allQuestions.Count);
                        if (!addedQuestions.Contains(questionIndex))
                        {
                            addedQuestions.Add(questionIndex);
                            AddQuestion(allQuestions.ElementAt(questionIndex).Value, rand);
                        }
                    }
                }
                else
                {
                    int questionsNum = allQuestions.Count;
                    HashSet<int> addedQuestions = new HashSet<int>();
                    Random randQuestion = new Random();
                    while (addedQuestions.Count < questionsNum)
                    {
                        int questionIndex = randQuestion.Next(0, allQuestions.Count);
                        if (!addedQuestions.Contains(questionIndex))
                        {
                            addedQuestions.Add(questionIndex);
                            AddQuestion(allQuestions.ElementAt(questionIndex).Value, rand);
                        }
                    }
                }
            }

        }
        public Test()
            : this(null)
        { }

        private void AddQuestion(Question fullQuestion, Random rand)
        {
            this.numberOfQuestions++;
            string correctAnswer = fullQuestion.GetCorrectAnswer();
            HashSet<string> wrongAnswers = fullQuestion.GetWrongAnswers(this.answersNumber-1);        
            int correct = rand.Next(0, wrongAnswers.Count + 1);
            string[] allAnswers = new string[wrongAnswers.Count + 1];
            int index = rand.Next(0, wrongAnswers.Count + 1);
            bool[] usedIndexes = new bool[wrongAnswers.Count + 1];
            foreach (string wrAnswer in wrongAnswers)
            {
                while(usedIndexes[index])
                {
                    index = rand.Next(0, wrongAnswers.Count + 1);
                }
                if (index == correct)
                {
                    allAnswers[index] = correctAnswer;
                    usedIndexes[index] = true;
                    while (usedIndexes[index])
                    {
                        index = rand.Next(0, wrongAnswers.Count + 1);
                    }
                    allAnswers[index] = wrAnswer;
                    usedIndexes[index] = true;
                }
                else if(!usedIndexes[index])
                {
                    allAnswers[index] = wrAnswer;
                    usedIndexes[index] = true;
                }            
            }
            QuestionForTest qt = new QuestionForTest(fullQuestion.QuestionText, allAnswers, correct, this.numberOfQuestions);
            this.testQuestions.Add(this.numberOfQuestions, qt);
        }

        private string Header(int testNumber)
        {
            StreamReader reader = new StreamReader("Header.html", Encoding.GetEncoding("windows-1251"));
            string head = "";
            using (reader)
            {
                head = reader.ReadToEnd();
                string testN = testNumber.ToString().PadLeft(3, '0');
                string replace = "Вариант " + testN;
                string pattern = @"Вариант 000";
                head = Regex.Replace(head, pattern, replace);
            }
            return head;
        }

        private string Foot()
        {
            StreamReader reader = new StreamReader("Foot.html", Encoding.GetEncoding("windows-1251"));
            string foot = "";
            using (reader)
            {
                foot = reader.ReadToEnd();
            }
            return foot;
        }

        public void GenerateTest(int testNumber)
        {
            string testName = "test" + testNumber.ToString().PadLeft(3, '0') + ".html";
            if(answers.ContainsKey(testNumber))
            {
                answers[testNumber].Clear();
            }
            else
            {
                answers.Add(testNumber, new List<char>());
            }
            using (Stream destStream = new FileStream(testName, FileMode.Create, FileAccess.Write))
            {
                StreamWriter writer = new StreamWriter(destStream, Encoding.GetEncoding("windows-1251"));
                using (writer)
                {
                    writer.WriteLine(this.Header(testNumber));
                    writer.WriteLine();
                    for (int i = 1; i <= this.testQuestions.Count; ++i)
                    {
                        QuestionForTest que = this.testQuestions[i];
                        StringBuilder sb = new StringBuilder("<p class=Question>" + i.ToString() + ". " + que.question + "</p>");
                        writer.WriteLine(sb.ToString());
                        writer.WriteLine();
                        for (int j = 0; j < que.answers.Length; j++)
                        {
                            sb.Clear();
                            sb.Append("<p class=Answer>");
                            sb.Append(Convert.ToChar(j + 97));
                            sb.Append(") ");
                            sb.Append(que.answers[j]);
                            sb.Append("</p>");
                            writer.WriteLine(sb.ToString());
                            writer.WriteLine();
                        }
                        switch (que.correct)
                        {
                            case 0:
                                answers[testNumber].Add('a');
                                break;
                            case 1:
                                answers[testNumber].Add('b');
                                break;
                            case 2:
                                answers[testNumber].Add('c');
                                break;
                            case 3:
                                answers[testNumber].Add('d');
                                break;
                            case 4:
                                answers[testNumber].Add('e');
                                break;
                            default:
                                answers[testNumber].Add('!');;
                                break;
                        }
                    }
                    writer.WriteLine(this.Foot());
                }
            }
        }

        private string HeaderForAnswers()
        {
            StreamReader reader = new StreamReader("HeaderForAnswers.html", Encoding.GetEncoding("windows-1251"));
            string head = "";
            using (reader)
            {
                head = reader.ReadToEnd();
            }
            return head;
        }

        private string FootForAnswers()
        {
            StreamReader reader = new StreamReader("FootForAnswers.html", Encoding.GetEncoding("windows-1251"));
            string foot = "";
            using (reader)
            {
                foot = reader.ReadToEnd();
            }
            return foot;
        }

        public void GenerateAnswers()
        {
            using (Stream destStream = new FileStream("answers.html", FileMode.Create, FileAccess.Write))
            {
                StreamWriter writer = new StreamWriter(destStream, Encoding.GetEncoding("windows-1251"));
                using (writer)
                {
                    writer.WriteLine(this.HeaderForAnswers());
                    int maxLenght = -1;
                    for (int j = 1; j <= answers.Count; j++)
                    {
                        if (answers[j].Count > maxLenght)
                        {
                            maxLenght = answers[j].Count;
                        }
                    }
                    StringBuilder sb = new StringBuilder();
                    for (int j = 1; j <= maxLenght; j++)
                    {
                        sb.Clear();
                        sb.Append("<td width=22 style='width:16.6pt;border:solid windowtext 1.0pt;border-left:\n");
                        sb.Append("none;padding:2.85pt 0cm 2.85pt 0cm;height:3.15pt'>\n");
                        sb.Append("<p class=MsoNormal align=center style='text-align:center;text-indent:0cm'\n");
                        sb.Append(string.Format("><span lang=EN-US style='font-size:10.0pt;font-family:Tahoma'>{0}</span></p>", j));
                        sb.Append("</td>\n");
                        writer.Write(sb.ToString());
                    }

                    for (int i = 1; i <= answers.Count; i++)
                    {
                        writer.WriteLine("</tr> <tr style='height:3.15pt'>");
                        List<char> answ = answers[i];
                        sb.Clear();
                        sb.Append("<td width=34 valign=top style='width:25.35pt;border:solid windowtext 1.0pt;\n");
                        sb.Append("padding:2.85pt 0cm 2.85pt 0cm;height:3.15pt'>\n");
                        sb.Append("<p class=MsoNormal align=center style='text-align:center;text-indent:0cm'><span\n");
                        sb.Append(string.Format("style='font-size:10.0pt;font-family:Tahoma'>{0}</span></p>", i.ToString().PadLeft(3, '0')));
                        sb.Append("</td>\n");
                        writer.Write(sb.ToString());
                        foreach (char item in answ)
                        {
                            sb.Clear();
                            sb.Append("<td width=22 style='width:16.6pt;border:solid windowtext 1.0pt;border-left:\n");
                            sb.Append("none;padding:2.85pt 0cm 2.85pt 0cm;height:3.15pt'>\n");
                            sb.Append("<p class=MsoNormal align=center style='text-align:center;text-indent:0cm'\n");
                            sb.Append(string.Format("><span lang=EN-US style='font-size:10.0pt;font-family:Tahoma'>{0}</span></p>", item));
                            sb.Append("</td>\n");
                            writer.Write(sb.ToString());
                        }
                    }

                    writer.WriteLine(this.FootForAnswers());
                }
            }
        }
    }
}
